/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef WORLD_H
#define WORLD_H
/*
* Simulation world that contains all simulation objects and takes care of
* performing simulation steps. Handles collisions and notifies primitives.
* Objects are updated on every simulation step.
* Also contains draw-function that calls draw on every object.
*
* TODO:
  -loading simulations from files
  -saving simulation states (needs features on all classes)
*/

#include <cstdlib>
#include <vector>
#include <string>

#include "ode/ode.h"

#include "Terrain.h"
#include "MapTypes.hpp"
#include "MapData.hpp"
#include "MapServer.hpp"
#include "binbag.h"

#include "OpenGLTextureLoader.hpp"

namespace gimi
{
  class GIMI;
}

class CObject;
//class CMaterialHandler;
class CGLProvider;

typedef std::vector<CObject*>::iterator ObjectsIter;

class CWorld
{
  public:
    dWorldID WorldID; //id of this world
    dSpaceID SpaceID; //this world's space id (CHECK: should there be more than 1?)
    dSpaceID StaticSpaceID; ///< Walls and other statics placed here, please


    //CMaterialHandler *MaterialHandler;

    //MLMA related variables
    //TODO: how to share these in a good way?
    float wallWidth, wallHeight;

    CWorld();//CGLProvider *GLProvider);

    ~CWorld();

    //gui calls this to draw simulation world
    //world calls draw-methods for objects in it
    void Draw();

    //opengl settings
    void InitGraphics();

    //init function for the world and objects in it
    //call this after creation and before starting to step
    void Activate();

    //take simulation step
    void Step();

    //set & get simulation step size
    void SetStepSize(float StepSize);
    float GetStepSize();

    void SetQuickMode(bool QuickMode);
    bool GetQuickMode();

    //get total time of simulation
    float GetSimulationTime();

    //used by mlma-loader to add objects
    void InsertObject(CObject* object);

    //
    bool RemoveObject(CObject* object);
    
    void  SetTerrain(CTerrain *terrain);
    
    CTerrain *GetTerrain(){ return terrain; }
    
    //get list of objects in world
    std::vector<CObject*> GetObjects();
    
    //dSpaceID Space; //space that contains all other spaces in this world

    CGLProvider* GetGLProvider();
    void SetGLProvider(CGLProvider* SetGLProvider);

    //gimi settings to use...
    void SetGimiSettingsFile(std::string file);
    //...or optionally hub params
    void SetHub(std::string hub, int port);
    
    //objects that want to use gimi must uset his to connect
    //this will ensure that all gimis are connected with same params
    bool ConnectGimi(gimi::GIMI *gimi, std::string name);


    void SetBaseMaCIGroup(std::string &aGroupName);
    std::string GetBaseMaCIGroup();


    void SetDrawGridMode(bool aMode)
    {
      iDrawGridMode = aMode;

    }

    bool GetDrawGridMode() { return iDrawGridMode; }

    /** Returns a pointer to the OpenGLTextureLoader
     *
     */
    COpenGLTextureLoader* GetOpenGLTextureLoader() { return iOpenGLTextureLoader; }

  private:

    //the duration of simulation since start
    float SimulationTime;
    //simulation step size in seconds
    float StepSize;

    //quick mode settings
    int QuickStepIters;
    bool QuickMode;

    //temp contacts used in collision detection
    dJointGroupID contactgroup;

    //simulation objects that world contains
    std::vector<CObject*> Objects;

    //vectors for storing objects temporarily between adds and removes
    std::vector<CObject*> ObjectsToAdd;
    std::vector<CObject*> ObjectsToRemove;
    
    CTerrain *terrain;
    
    CGLProvider *GLProvider;
    COpenGLTextureLoader *iOpenGLTextureLoader;

    bool graphicsInited;

    bool useGimiSettingsFile;
    std::string gimiSettingsFile;
    std::string hubAddress;
    int hubPort;
    
    MaCI::MaCICtrl::CMaCICtrlServer *iMCS;
    MaCI::Map::CMapServer *iLineMapServer;
    int iNumberOfObjectInMap;
    bool iLineMapServerConstructed;
    std::string iBaseMaCIGroup;

    bool iDrawGridMode; ///< We draw the XY grid if this is true

    //update objects
    void UpdateObjects();

    void UpdateMapServer(std::vector<CWall *> walls, bool add);
    //adds objects that are waiting for add
    void AddObjects();
    //removes objects that are waiting for remove
    void RemoveObjects();

    void CreateMapServer();    
    //collision callback that is given to ode
    static void NearCallback(void *data, dGeomID o1, dGeomID o2);

    //calculates a contact between 2 geoms
    dSurfaceParameters getContact(dGeomID ID1, dGeomID ID2);

    bool iMakeNewWallDisplayList; ///< When this is false, all walls are drawn in a display list
    GLuint iWallDisplayListID; ///< Display list ID for the walls.
    int iTotalWalls;           ///< Number of walls in the world. If this changes, we remake the wallDisplayListID
};

#endif //WORLD_H
